package com.yeskery.nut.util;

import com.yeskery.nut.core.JavaVersion;
import com.yeskery.nut.core.NutException;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * json工具类
 * @author sprout
 * 2022-05-17 11:07
 */
public class JsonUtils {

    /** 日志对象 */
    private static final Logger logger = Logger.getLogger(JsonUtils.class.getName());

    /**
     * 私有化构造方法
     */
    private JsonUtils() {
    }

    /**
     * JVM 自带的 JS 引擎
     */
    private final static ScriptEngine ENGINE = engineFactory();

    /**
     * 创建 js 引擎工厂，支持 java 6/7 的 rhino 和 java 8 的 nashorn
     *
     * @return js 引擎
     */
    public static ScriptEngine engineFactory() {
        return new ScriptEngineManager().getEngineByName(JavaVersion.getJavaVersion().isEqualOrNewerThan(JavaVersion.EIGHT) ? "nashorn" : "rhino");
    }

    /**
     * 读取 json 里面的 map
     *
     * @param js
     *      JSON 字符串
     * @param key
     *      JSON Path，可以带有 aa.bb.cc
     * @return Map 对象
     */
    @SuppressWarnings("unchecked")
    public static Map<String, Object> getMap(String js, String key) {
        return (Map<String, Object>) accessMember(js, key, Map.class);
    }

    /**
     * 读取 json 里面的 map
     *
     * @param js
     *      JSON 字符串
     * @return Map 对象
     */
    public static Map<String, Object> getMap(String js) {
        return getMap(js, null);
    }

    /**
     * 转换为 map 或 list
     *
     * @param js JSON 字符串
     * @param key JSON Path，可以带有 aa.bb.cc
     * @param clazz 目标类型
     * @param <T> 转换后的类型
     * @return 目标对象
     */
    @SuppressWarnings("unchecked")
    public static <T> T accessMember(String js, String key, Class<T> clazz) {
        T result;

        try {
            // rhino 不能直接返回 map，如 eval("{a:1}")
            ENGINE.eval("var obj = " + js);
            // -->null，必须加变量，例如 执行 var xx =
            // {...};
            Object obj;
            if (key == null) {
                obj = ENGINE.eval("obj;");
            } else {
                if (key.contains(".")) {
                    obj = ENGINE.eval("obj." + key + ";");
                } else {
                    obj = ENGINE.eval("obj['" + key + "'];");
                }
            }
            result = (T) obj;
        } catch (ScriptException e) {
            throw new NutException("Json String Parse Fail.", e);
        }

        return result;
    }

    /**
     * 读取 json 里面的 list，list 里面每一个都是 map
     * @param js JSON 字符串
     * @param key JSON Path，可以带有 aa.bb.cc
     * @return 包含 Map 的列表
     */
    @SuppressWarnings("unchecked")
    public static List<Map<String, Object>> getList(String js, String key) {
        return (List<Map<String, Object>>) accessMember(js, key, List.class);
    }

    /**
     * 读取 json 里面的 list，list 里面每一个都是 map
     * @param js JSON 字符串
     * @return 包含 Map 的列表
     */
    public static List<Map<String, Object>> getList(String js) {
        return getList(js, null);
    }

    /**
     * 读取 json 里面的 list，list 里面每一个都是 String
     * @param js JSON 字符串
     * @param key JSON Path，可以带有 aa.bb.cc
     * @return 包含 String 的列表
     */
    @SuppressWarnings("unchecked")
    public static List<String> getStringList(String js, String key) {
        return (List<String>) accessMember(js, key, List.class);
    }

    /**
     * 读取 json 里面的 list，list 里面每一个都是 String
     * @param js JSON 字符串
     * @return 包含 String 的列表
     */
    public static List<String> getStringList(String js) {
        return getStringList(js, null);
    }

    /**
     * js number 为 double 类型，在 java 里面使用不方便，将其转换为 int
     * @param d number
     * @return int 值
     */
    public static int double2int(Double d) {
        if (d > Integer.MAX_VALUE) {
            logger.warning("double value :[" + d + "] is greater than int's max value");
            return 0;
        } else {
            return d.intValue();
        }

    }

    /**
     * 简单的将对象转换为String，不支持嵌套对象
     * @param obj 对象
     * @return 转换后的
     */
    public static String simpleWriteObjectAsString(Object obj) {
        if (obj == null) {
            return null;
        }

        if (obj instanceof String) {
            return (String) obj;
        }

        Class<?> clazz = obj.getClass();
        java.lang.reflect.Field[] fields = clazz.getDeclaredFields();

        StringBuilder buff = new StringBuilder();

        buff.append("{");

        for (java.lang.reflect.Field f : fields) {
            String fieldName; Object value;

            try{
                fieldName = f.getName();
                f.setAccessible(true);
                if(fieldName.contains("this$")) {
                    continue;
                }
                value = f.get(obj);

                buff.append("\"");
                buff.append(fieldName);
                buff.append("\":");

                if(value == null){
                    buff.append("\"\",");
                    continue;
                }
                if(value instanceof Boolean){
                    buff.append(value);
                    buff.append(",");
                }else if(value instanceof Number){
                    buff.append(value);
                    buff.append(",");
                }else if(value instanceof java.util.Date){
                    buff.append("\"");
                    buff.append(new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((java.util.Date)value));
                    buff.append("\",");
                }else if(value instanceof Object[]){
                    Object[] arr = (Object[])value;
                    StringBuilder str = new StringBuilder();

                    for(int i = 0; i < arr.length; i++){
                        if(arr[i] instanceof String){
                            str.append(("\"" + arr[i] + "\"").replace("\\", "\\\\"));
                        }else{
                            str.append(simpleWriteObjectAsString(arr[i]));
                        }

                        if(i != arr.length - 1) {
                            str.append(",");
                        }
                    }

                    buff.append("[");
                    buff.append(str);
                    buff.append("]o");
                }else{
                    buff.append("\"");
                    buff.append(value.toString().replace("\\", "\\\\").replace("\"",
                            "\\\""));
                    buff.append("\",");
                }

            }catch(Exception e){
                logger.logp(Level.SEVERE, JsonUtils.class.getName(), "simpleWriteObjectAsString",
                        "JSON Parse Fail.", e);
            }
        }
        if (buff.length() > 1) {
            buff.deleteCharAt(buff.length() - 1);
        }
        buff.append("}");
        return buff.toString();
    }
}
